package com.tyk.bookstore.back.platform.service.impl;

import com.tyk.bookstore.back.common.core.model.type.BinVo;
import com.tyk.bookstore.back.common.core.util.AssertUtil;
import com.tyk.bookstore.back.common.core.util.ObjUtil;
import com.tyk.bookstore.back.platform.model.entity.AreaDo;
import com.tyk.bookstore.back.platform.model.result.AreaResultEnum;
import com.tyk.bookstore.back.platform.repository.AreaRepository;
import com.tyk.bookstore.back.platform.service.AreaApiService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

import java.math.BigInteger;
import java.util.*;

/**
 * @author tyk
 * @description AreaApiServiceImpl
 */
@Service
@RequiredArgsConstructor
public class AreaApiServiceImpl implements AreaApiService {

    private final AreaRepository areaRepository;



    @Override
    public List<BinVo> getFullNameListByCodeSet(Set<Long> codeSet) {
        if (codeSet.isEmpty())
            return new ArrayList<>();
        HashMap<Long, String> map = new HashMap<>();
        return codeSet.stream().map(it -> new BinVo(it, buildAreaFullNameWithCache(it, map))).toList();
    }

    @Override
    public String getFullNameByCode(Long lowCode) {
        return buildAreaFullName(lowCode);
    }

    @Override
    public void assertLastAreaByCode(Long code) {
        AssertUtil.isTrue(
                areaRepository.exist1Eq(AreaDo::getCode, code, AreaDo.class),
                AreaResultEnum.AREA_NOT_FOUND
        );
        AssertUtil.isFalse(
                areaRepository.exist1Eq(AreaDo::getParentCode, code, AreaDo.class),
                AreaResultEnum.AREA_NOT_LOWEST
        );
    }

    @Override
    public List<Long> getLeafCodeListByCode(Long code) {
        return findLeafCodeList(code);
    }


    /**
     * 构建区域全称
     */
    private String buildAreaFullNameWithCache(Long code, Map<Long, String> map) {
        if (map.containsKey(code)) return map.get(code);
        com.tyk.bookstore.back.common.dal.mapper.dto.BinDto<BigInteger, String> dto = areaRepository.selectBin1Eq(
                AreaDo::getParentCode, AreaDo::getName,
                AreaDo::getCode, code,
                AreaDo.class
        );
        String fullName;
        if (dto.getV1().intValue() == 0) fullName = dto.getV2();
        else fullName = buildAreaFullNameWithCache(dto.getV1().longValue(), map) + dto.getV2();
        map.put(code, fullName);
        return fullName;
    }

    /**
     * 构建区域全称
     */
    private String buildAreaFullName(Long areaCode) {
        com.tyk.bookstore.back.common.dal.mapper.dto.BinDto<BigInteger, String> dto = areaRepository.selectBin1Eq(
                AreaDo::getParentCode, AreaDo::getName,
                AreaDo::getCode, areaCode,
                AreaDo.class
        );
        return dto.getV1().intValue() == 0 ? dto.getV2() : buildAreaFullName(dto.getV1().longValue()) + dto.getV2();
    }


    /**
     * 找到以code为根结点的树的所有叶子结点
     */
    private List<Long> findLeafCodeList(Long code) {
        List<Long> subCodeList = areaRepository
                .<BigInteger>selectSinList1Eq(
                        AreaDo::getCode,
                        AreaDo::getParentCode, code,
                        AreaDo.class
                )
                .stream().map(it -> it.getV1().longValue()).toList();
        if (ObjUtil.isEmpty(subCodeList)) return List.of(code);
        List<Long> rs = new ArrayList<>();
        subCodeList.forEach(id -> rs.addAll(findLeafCodeList(id)));
        return rs;
    }
}
